#ifdef WITH_OPENGL
#pragma once

#include "opengl.h"
#include "index_buffer.h"
#include "context_opengl.h"
#include "renderer.h"
#include "scene_exception.h"

namespace krryn{
namespace scene{
	template<>
	class index_buffer<system::opengl_tag> : public hardware_buffer{
	public:
		typedef krryn::core::refc_ptr<index_buffer> ptr;

		index_buffer(system::renderer<system::opengl_tag> &a_Renderer, size_t a_Size)
			: m_Context(a_Renderer.get_context()), m_Size(a_Size), m_Count(0){
			glGenBuffersARB(1, &m_BufferId);
			glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, m_BufferId);
			glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, sizeof(size_t) * a_Size, 0, GL_STATIC_DRAW_ARB);
		}

		virtual void lock(lock_type a_Type){
			hardware_buffer::lock(a_Type);
			bind();

			if(locked() != read){
				map();
			}
		}

		virtual void unlock(){
			hardware_buffer::unlock();
			if(locked() != read){
				unmap();
			}
			unbind();
		}

		void operator +=(size_t a_Index){
			if(locked() ^ write){
				throw scene_exception("Buffer must be write locked for insertion");
			}

			if(m_Count >= m_Size){
				throw scene_exception("Buffer out of bounds");
			}

			*m_MappedBuffer++ = a_Index;
			m_Count++;
		}

		size_t size() const{
			return m_Size;
		}
	private:
		void bind(){
			glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, m_BufferId);
			m_Context.enable(GL_INDEX_ARRAY);
		}

		void unbind(){
			glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
			m_Context.disable(GL_INDEX_ARRAY);
		}

		void map(){
			m_Count = 0;
			m_MappedBuffer = (size_t*)glMapBufferARB(
				GL_ELEMENT_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
		}

		void unmap(){
			glUnmapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB);
			m_MappedBuffer = 0;
		}

		size_t *m_MappedBuffer;
		GLuint m_BufferId;
		size_t m_Count;
		size_t m_Size;
		system_imp::context_opengl &m_Context;
	};
}
}

#endif